package com.luoye.apt;

import com.google.auto.service.AutoService;
import com.google.gson.Gson;
import com.luoye.bindview.BindView;
import com.luoye.bindview.OnClick;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.Writer;
import java.util.*;

@AutoService(Processor.class)
public class BindViewAPT extends AbstractProcessor {

    private Filer filer;

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> set=new HashSet<>();
        set.add(BindView.class.getCanonicalName());
        //set.add(ActivityBinding.class.getCanonicalName());
        set.add(OnClick.class.getCanonicalName());
        return set;
    }

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();
        log("自定义的APT开始工作了");

    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (annotations.size()==0){
            return false;
        }
        log("开始循环APT 节点");
        log(BindView.class.getSimpleName());
        log(BindView.class.getName());
        log(BindView.class.getCanonicalName());
        Map<String,Map<String, List<Element>>> simpleMap=new HashMap();
        Map<String,String> simplePackageMap=new HashMap<>();
        for (TypeElement element: annotations){
            log(element.toString());
            Set<? extends Element> annotatedWith = roundEnv.getElementsAnnotatedWith(element);
            for (Element child: annotatedWith){
                log("子节点："+child.toString());
                String simpleName = child.getEnclosingElement().getSimpleName().toString();
                if (!simpleMap.containsKey(simpleName)){
                    Map<String,List<Element>> childMap=new HashMap<>();
                    childMap.put(element.getSimpleName().toString(),new ArrayList<>());
                    simpleMap.put(simpleName,childMap);
                    String packageName = processingEnv.getElementUtils().getPackageOf(child).toString();
                    simplePackageMap.put(simpleName,packageName);
                }else {
                    if (!simpleMap.get(simpleName).containsKey(element.getSimpleName().toString())){
                        simpleMap.get(simpleName).put(element.getSimpleName().toString(),new ArrayList<>());
                    }
                }
                simpleMap.get(simpleName).get(element.getSimpleName().toString()).add(child);
                log(child.getEnclosingElement().getSimpleName().toString());
            }
        }
        log("APT 节点循环结束");
        for (String simpleName:  simpleMap.keySet()){
            log(simpleName+simplePackageMap.get(simpleName));
            try {
                createBindClass(simpleName,simplePackageMap.get(simpleName),simpleMap.get(simpleName));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            for (String element: simpleMap.get(simpleName).keySet()){
                log(simpleName+"  "+element);
                for (Element child: simpleMap.get(simpleName).get(element)){
                    log(simpleName+" "+element+ " "+child.getSimpleName());
                }
            }
        }

        // 表示注解处理完成。
        return true;
    }

    /**
     * 这里的是一个class内的所有标记。
     *
     * @param simpleName
     * @param packageName
     * @param elementListMap
     */
    private void createBindClass(String simpleName, String packageName, Map<String, List<Element>> elementListMap) throws IOException {
        String bindView = BindView.class.getSimpleName();
        String onClick = OnClick.class.getSimpleName();
        //JavaPoetUtils.createClass(filer,simpleName,packageName);
        //如果两个都不存在，就不写文件了。
        if (!(elementListMap.containsKey(bindView)&&elementListMap.containsKey(onClick))){
            return;
        }else if (elementListMap.containsKey(bindView)&&elementListMap.containsKey(onClick)){
            // 如果两个都存在，但是都是等于0，也不写。
            if (elementListMap.get(bindView).size()==0&&elementListMap.get(onClick).size()==0){
                return;
            }
        }
        List<Element> bindViews = elementListMap.get(bindView);
        List<Element> onClicks = elementListMap.get(onClick);
        Map<Integer,String> views=new HashMap<>();
        try {
            JavaFileObject sourceFile=filer.createSourceFile(packageName+"."+simpleName+"Binding");
            Writer writer = sourceFile.openWriter();
            writer.write("package "+packageName+";\n");
            writer.write("import com.luoye.bindview.Bind;\n");
            writer.write("class "+simpleName+"Binding implements Bind<"+packageName+"."+simpleName+">{\n");
            writer.write("@Override\n");
            writer.write("public void binding("+packageName+"."+simpleName+" object){\n");
            writer.write("bindView(object);\n");
            writer.write("bindClick(object);\n");
            writer.write("\n}");
            writer.write("@Override\n");

            writer.write("public void bindView("+packageName+"."+simpleName+" object){\n");
            for (Element element: bindViews){
                String variableName = element.getSimpleName().toString();
                int id= element.getAnnotation(BindView.class).id();
                TypeMirror typeMirror = element.asType();
                log(variableName + "  "+id +"   "+typeMirror.toString());
                views.put(id,variableName);
                writer.write("object."+variableName+"=("+typeMirror+")object.findViewById("+id+");\n");
            }
            writer.write("\n}\n");
            writer.write("@Override\n");
            writer.write("public void bindClick("+packageName+"."+simpleName+" object){\n");
            for (Element element: onClicks){
                String variableName = element.getSimpleName().toString();
                int [] ids= element.getAnnotation(OnClick.class).ids();
                for (int id: ids){
                    if (views.containsKey(id)){
                        String viewName = views.get(id);
                        writer.write("object."+viewName+".setOnClickListener(v -> {object."+variableName+"(v);});\n");
                    }else {
                        writer.write("object.findViewById("+id+").setOnClickListener(v -> {object."+variableName+"(v);});\n");
                    }
                }
                log(variableName + "  "+new Gson().toJson(ids) +"   ");
            }
            writer.write("\n}\n");
            writer.write("\n}");
            writer.close();
        }catch (Exception e){

        }

    }

    public void log(String log){
        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,log);
    }
}
